import {piniaSymbol} from './rootState'
import {getCurrentInstance,inject,reactive,effectScope,computed,isRef,isReactive} from 'vue'
// 1. 先处理命名空间如果是{id:''} 或者 字符串 
// 2. 返回一个函数，用户拿到后调用可以获取到store
// 3. 通过getCurrentInstance 获取app组件实例，然后通过inject然后获取pinia实例
// 4. 通过pinia._s 来获取仓库 如果当前仓库不存在进行初始化 如果存在进行获取返回
// 5. 处理仓库初始化的操作 创建一个createOptions 进行仓库映射
// 6. 对于全局pinia可以直接调用_e.stop停止所有仓库响应时，store可以自己停止自己
// 7. 创建局部仓库映射到全局_s存储中，并返回。通过reactive包裹
// 8. 处理局部仓库内容，然后添加到仓库中
// 9. 将actions映射到仓库中，并且如果是结构导入{increment()}调用时 this应该正确指向当前store
// 10. 将getters映射到仓库中（将其处理成对象形式）, 使用computed包裹并且要保持this,然后可以通过key直接获取到返回内容，无需调用函数


// 计算属性是一个ref 同时也是一个effect
function isComputed(prop){
  return !!(isRef(prop) && prop.effect)
}

export function defineStore(idOrOptions,setup){
  let id = null
  let options = null

  if(typeof idOrOptions === 'string'){
    // setup的方式配置
    id = idOrOptions
    options = setup
    // TODO setup方式待实现
  }else if(idOrOptions.id) {
    // options 方式
    options = idOrOptions
    id = idOrOptions.id
  }

  // 判断是不是stup方式
  const isSteupStore = typeof setup === 'function'
  // 处理数据
  function useStore(){
    // 获取全局pinia getCurrentInstance() app组件实例
    const instance = getCurrentInstance()
    const pinia = instance && inject(piniaSymbol)
    if(!pinia._s.has(id)){ // 第一次store
      if(isSteupStore){
        // 用户自定义函数
        createSteupStore(id,setup,pinia,isSteupStore)
      }else{
         // 仓库映射
        createOptionsStore(id,options,pinia,isSteupStore)
      }
    }
    const store = pinia._s.get(id)
    console.log(pinia.state.value)
    return store
  }

  return useStore // 用户最终函数调用可以获取到state
}
/**
 * 初始化store options版本
 * @param {*} id 仓库id
 * @param {*} options  用户选项：{state,getters,actions}
 * @param {*} pinia 全局pinia实例
 */
function createOptionsStore(id,options,pinia){
  const {state,getters,actions} = options
  function setup(){
    // 如果当前仓库的state不存在 那么赋值空对象,并且将当前的state 保存到全局pinia的state中
    const localState = pinia.state.value[id] = state ? state()  : {}
    // getters 可能为空
    return Object.assign(localState,actions,Object.keys(getters || {}).reduce((meno,name)=>{
      // 处理getters将其映射为响应，并且保持this
      meno[name] = computed(()=>{
        const store = pinia._s.get(id)
        return getters[name].call(store)
      })
      return meno
    },{}))
  }
  createSteupStore(id,setup,pinia)
}

function createSteupStore(id,setup,pinia,optionsApi){
  const store = reactive({})
  // 处理 全局停用，自己停止自己
  let scope = null

  // setup 的state为空
  const initiaState = pinia.state.value[id]
  if(!initiaState && optionsApi){
    pinia.state.value[id] = {}
  }

  // 先用全局scope包裹局部scope，这样就可以实现全局停用，和局部停用
  // pinia._e.stop
  // scope.stop
  const steupStore = pinia._e.run(()=>{
    scope = effectScope()
    return scope.run(()=>setup())
  })

  // 处理actions的this指向问题
  function warpAction(key,action){
    return function(){
      let ret = action.apply(store,arguments)
      // TODO 如果actions执行后是Promise
      return ret
    }
  }
  
  for(let key in steupStore){
    const prop = steupStore[key]
    if(typeof prop === 'function'){
      // 对action中的this 和 后续逻辑进行处理，函数劫持
      steupStore[key] = warpAction(key,prop)
    }
    // 看看这个值是不是状态
    // computed他也是ref
    if(isRef(prop) &&  !isComputed(prop)  || isReactive(prop)){
      if(optionsApi){
        // 是setup
        pinia.state.value[id][key] = prop
      }
    }
  }
  // 映射全局存储
  pinia._s.set(id,store) // 将id于store映射
  Object.assign(store,steupStore)
  return store
}